package me.boot.db.redis;

import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import me.base.utils.StrPool;
import me.boot.db.redis.cache.CacheProps;
import me.boot.db.redis.serializer.RedisObjectSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * @Description:
 * @author: Sylar
 * @date: 2021年10月20日 5:05 下午
 */
@Slf4j
@AutoConfigureAfter(RedisConfiguration.class)
@EnableCaching
@Configurable
public class CacheConfiguration {

    @Autowired
    CacheProps cacheProps;

    @PostConstruct
    void init() {
        log.info("Init CacheConfiguration");
    }

    /**
     * key 的生成
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return (target, method, objects) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(StrPool.COLON);
            sb.append(method.getName());
            for (Object obj : objects) {
                if (obj != null) {
                    sb.append(StrPool.COLON);
                    sb.append(obj);
                }
            }
            return sb.toString();
        };
    }

    /**
     * 用于 @Cacheable 相关注解
     *
     * @param redisConnectionFactory 链接工厂
     * @return 缓存管理器
     */
    @Bean
    @Primary
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration cacheConfig = getCacheConfig();

        Map<String, CacheProps.Cache> custom = cacheProps.getCustom();
        Map<String, RedisCacheConfiguration> map = Maps.newHashMap();
        //自定义的缓存过期时间配置
        Optional.ofNullable(custom).ifPresent(
                config -> config.forEach(
                        (key, cache) -> {
                            RedisCacheConfiguration cfg = configFromProperty(cacheConfig, cache);
                            map.put(key, cfg);
                        }));

        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(cacheConfig)
                .withInitialCacheConfigurations(map)
                .build();
    }


    private RedisCacheConfiguration getCacheConfig() {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .disableCachingNullValues()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new RedisObjectSerializer()));
        return configFromProperty(config, cacheProps.getDef());
    }

    private RedisCacheConfiguration configFromProperty(RedisCacheConfiguration config, CacheProps.Cache cache) {
        if (Objects.isNull(cache)) {
            return config;
        }
        if (cache.getTtl() != null) {
            config = config.entryTtl(cache.getTtl());
        }
        if (cache.getKeyPrefix() != null) {
            config = config.computePrefixWith(cacheName -> cache.getKeyPrefix().concat(StrPool.COLON).concat(cacheName).concat(StrPool.COLON));
        } else {
            config = config.computePrefixWith(cacheName -> cacheName.concat(StrPool.COLON));
        }
        if (!cache.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!cache.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }

        return config;
    }

}
